﻿using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Wicture.DbRESTFul;
using Wicture.DbRESTFul.SQL;
using Wicture.DTT.ETK.Protocol;
using Wicture.EPC.Common;
using Wicture.EPC.Service.V1.Models;
using Wicture.EPC.Service.V1.Models.BMW;
using Wicture.EPC.Service.V1.Repository;
using Dapper;
using Wicture.EPC.Service.V1.Services.EPC.OnlineQuery;
using Wicture.EPC.Service.V1.Models.Stock;

namespace Wicture.EPC.Service.V1.QueryInterceptors.Interceptors
{
    public class BmwQueryInterceptor : BaseQueryInterceptor
    {
        private string[] brandCodes = new string[] { "bmw", "mini", "rolls_royce", "zinoro" };
        private static readonly string _EmptySnSign = "--";
        private static readonly string _NouseSnSign = "nouse";

        public override string[] BrandCodes => brandCodes;

        public override object QueryHotPoints(DbRESTFulRepository repository, JObject param)
        {
            IEnumerable<dynamic> data = repository.Invoke(CSIs.Query_HotPoints_For_Bmw, param);
            if (data.Count() == 0) return null;
            return new
            {
                imgs = new List<Query_HotPointsResult._imgs> {
                    new Query_HotPointsResult._imgs {
                        url =data.FirstOrDefault().url,
                        points =JArray.FromObject(data).ToObject<List<Query_HotPointsResult._imgs._points>>()
                    }
                }
            };
        }

        public override object QueryModel(DbRESTFulRepository repository, JObject request)
        {
            var param = request.ToObject<Get_Model_InfoParam>();
            Get_Model_InfoResult result = null;
            string featureCodes = null;
            dynamic model = null;
            if (!string.IsNullOrEmpty(param.vin))
            {
                var sequence = GetSequenceForVin(param.vin);
                if (sequence != null)
                {
                    string suffix = sequence.Substring(0, 2);
                    model = repository.Invoke(CSIs.Get_Model_By_Accuracy_Vin_For_Bmw, new { sequence, suffix });
                    if (model == null)
                    {
                        try
                        {
                            LoggerManager.Logger.LogInformation($"Start call QueryBmw for vin: { sequence }.");
                            var vinInfo = VinQueryRpcService.Query(new QPZS.ETK.VinRequest { Code = sequence }).Result;
                            LoggerManager.Logger.LogInformation($"Result from QueryBmw: { JsonConvert.SerializeObject(vinInfo, Formatting.Indented)}.");
                            if (vinInfo != null && !vinInfo.HasError && vinInfo.Data != null && !string.IsNullOrEmpty(vinInfo.Data.Vin))
                            {
                                repository.Invoke(CSIs.Insert_Vin_For_Bmw, vinInfo.Data);
                                model = repository.Invoke(CSIs.Get_Model_By_Accuracy_Vin_For_Bmw, new { sequence, suffix });
                            }
                        }
                        catch
                        {
                            throw new Exception("宝马VIN服务查询失败！");
                        }
                    }
                    if (model == null)
                    {
                        model = repository.Invoke(CSIs.Get_Model_By_Vin_For_Bmw, new { sequence, suffix });
                    }
                    if (model == null)
                    {
                        throw new LogicalException($"未找到该vin码(BMW):{param.vin}", 610);
                    }

                    if (param.vin.Length == 17) model.vin = param.vin.ToUpper();
                    else model.vin = $"{model.prefix}{param.vin}".ToUpper();
                    var checkno = VinHelper.GetCheckNo(model.vin);
                    if (string.IsNullOrEmpty(checkno))
                    {
                        throw new LogicalException($"未找到该vin码(BMW):{param.vin}", 610);
                    }
                    var ninthno = model.vin.Substring(8, 1);
                    if (ninthno == "*")
                    {
                        model.vin = model.vin.Replace("*", checkno);
                    }
                    else
                    {
                        if (ninthno != checkno)
                        {
                            throw new LogicalException($"未找到该vin码(BMW):{param.vin}", 610);
                        }
                    }
                }
                else
                {
                    return null;
                }
            }
            else
            {
                var ids = param.id.Split("_");
                if (ids.Count() > 1)
                {
                    param.id = ids[1];
                }
                model = repository.Invoke(CSIs.Get_Model_For_Bmw, param);
            }

            if (model != null)
            {
                featureCodes = model.featureCodes;
                result = JObject.FromObject(model).ToObject<Get_Model_InfoResult>();
                if (!string.IsNullOrEmpty(param.vin))
                {
                    result.description = $"{model.NAME}({model.region})({model.chassisName})>{model.productionDate.ToString().Split('-')[0]}>{model.motor}({model.capacity})>{model.transmission}";
                }
                var props = new List<Get_Model_InfoResult._props>();
                if (!string.IsNullOrEmpty(model.NAME)) props.Add(new Get_Model_InfoResult._props { key = "车型", value = model.NAME });
                if (!string.IsNullOrEmpty(model.region)) props.Add(new Get_Model_InfoResult._props { key = "区域", value = model.region });
                if (!string.IsNullOrEmpty(model.motor)) props.Add(new Get_Model_InfoResult._props { key = "发动机", value = model.motor });
                if (!string.IsNullOrEmpty(model.typeCode)) props.Add(new Get_Model_InfoResult._props { key = "销售代码", value = model.typeCode });
                if (!string.IsNullOrEmpty(model.power)) props.Add(new Get_Model_InfoResult._props { key = "功率", value = model.power });
                if (!string.IsNullOrEmpty(model.upholstery)) props.Add(new Get_Model_InfoResult._props { key = "内饰", value = model.upholstery });
                if (!string.IsNullOrEmpty(param.vin))
                {
                    props.Add(new Get_Model_InfoResult._props { key = "车架号", value = model.vin });
                }
                if (!string.IsNullOrEmpty(model.seriesName)) props.Add(new Get_Model_InfoResult._props { key = "系列", value = model.seriesName });
                if (!string.IsNullOrEmpty(model.chassisName)) props.Add(new Get_Model_InfoResult._props { key = "底盘", value = model.chassisName });
                if (!string.IsNullOrEmpty(model.capacity)) props.Add(new Get_Model_InfoResult._props { key = "排量", value = model.capacity });
                if (!string.IsNullOrEmpty(model.drive)) props.Add(new Get_Model_InfoResult._props { key = "驱动", value = model.drive });
                if (!string.IsNullOrEmpty(model.productionDate)) props.Add(new Get_Model_InfoResult._props { key = "生产日期", value = model.productionDate });
                if (!string.IsNullOrEmpty(model.color)) props.Add(new Get_Model_InfoResult._props { key = "车身颜色", value = model.color });
                if (!string.IsNullOrEmpty(model.transmission)) props.Add(new Get_Model_InfoResult._props { key = "变速箱", value = model.transmission });
                if (!string.IsNullOrEmpty(model.description))
                {
                    var descList = ((string)model.description).Split(' ');
                    if (descList.Length > 3)
                    {
                        var hand = descList[descList.Length - 3];
                        props.Add(new Get_Model_InfoResult._props { key = "驾驶", value = hand == "R" ? "右座驾驶型" : "左座驾驶型" });
                    }
                }
                if (!string.IsNullOrEmpty(model.body)) props.Add(new Get_Model_InfoResult._props { key = "车体", value = model.body });
                if (!string.IsNullOrEmpty(model.doors)) props.Add(new Get_Model_InfoResult._props { key = "车门数", value = model.doors });
                result.props = props;
            }

            if (!string.IsNullOrEmpty(featureCodes))
            {
                var featureCodeList = featureCodes.Split(",");
                IEnumerable<Feature> features = repository.Invoke<Feature>(CSIs.Query_Vin_Features_For_Bmw, new { featureCodes = featureCodeList });

                result.features = new List<Get_Model_InfoResult._features>();
                foreach (var feature in features)
                {
                    result.features.Add(new Get_Model_InfoResult._features() { key = feature.featureCode, value = feature.featureName });
                }
            }
            return result;
        }

        protected override List<SearchSubGroupResults> GetSearchResults(DbRESTFulRepository repository, string brandCode, string[] partCodes, string selectorId)
        {
            int defaultTimeOut = CSILoader.Instance.Setting.CommandTimeout;
            CSILoader.Instance.Setting.CommandTimeout = 10;
            try
            {
                var ret = repository.Invoke<SearchSubGroupResults>(CSIs.Search_SubGroups_By_PartCodes_For_BMW, new { brandCode, partCodes, selectorId });
                CSILoader.Instance.Setting.CommandTimeout = defaultTimeOut;
                return ret;
            }
            catch
            {
                CSILoader.Instance.Setting.CommandTimeout = defaultTimeOut;
                throw new LogicalException("查询结果太多，请重新输入", 612);
            }
        }

        public override object QueryPartDetail(DbRESTFulRepository repository, JObject param)
        {
            var brandCode = param.Value<string>(ParamFields._brandCode);
            var partId = param.Value<string>(ParamFields._partId);
            dynamic data = null;
            if (string.IsNullOrEmpty(partId))
            {
                data = repository.Invoke(CSIs.Get_Part_Detail_For_Bmw, param);
            }
            else
            {
                data = repository.Invoke(CSIs.Get_Part_Usage_For_Bmw, param);
            }
            if (data == null) return null;
            // 1、基本信息赋值
            Part_Detail_Bmw partDetail = JObject.FromObject(data).ToObject<Part_Detail_Bmw>();
            Query_Part_DetailResult result = new Query_Part_DetailResult
            {
                cost = partDetail.cost,
                price = partDetail.price,
                priceUpdatedAt = partDetail.priceUpdatedAt,
                brandCode = brandCode,
                brandName = BrandDictionary.GetName(brandCode),
                partCode = partDetail.partCode,
                partName = partDetail.partName,
                partNameEn = partDetail.partNameEn,
                replacementPartNo = partDetail.replacementPartNo,
                quantity = partDetail.quantity,
                stock = partDetail.stock,
                stockInfos = partDetail.stockInfos
            };

            //3.替换件处理
            if (!string.IsNullOrEmpty(result.replacementPartNo))
            {
                List<Part_Detail_Bmw> partDetails = repository.Invoke<Part_Detail_Bmw>(CSIs.Query_Parts_By_Codes, new { partCodes = result.replacementPartNo.Split(',').Where(q => q != result.partCode) });
                var replacements = partDetails.OrderByDescending(q => q.partEndTime).Select(item => new Query_Part_DetailResult._replacement
                {
                    partCode = item.partCode,
                    partName = item.partName,
                    cost = item.cost,
                    price = item.price,
                    priceUpdatedAt = item.priceUpdatedAt,
                    brandCode = brandCode,
                    brandName = BrandDictionary.GetName(brandCode),
                    props = new List<Query_Part_DetailResult._prop> { new Query_Part_DetailResult._prop { key = "中断日期", value = item.partEndTime } }
                }).ToList();
                result.replacements = replacements;
            }

            var partStartTime = partDetail.partStartTime;
            if (partStartTime != null && partStartTime.Length > 6)
            {
                partStartTime = partStartTime.Substring(0, 6);
            }
            var partEndTime = partDetail.partEndTime;
            if (partEndTime != null && partEndTime.Length > 6)
            {
                partEndTime = partEndTime.Substring(0, 6);
            }
            //4.配件附加属性处理
            var props = new List<Query_Part_DetailResult._prop>
            {
                new Query_Part_DetailResult._prop { key = "英文名", value = partDetail.partNameEn ?? string.Empty },
                new Query_Part_DetailResult._prop { key = "件数", value = partDetail.quantity ?? string.Empty },
                new Query_Part_DetailResult._prop { key = "备注", value = partDetail.remark ?? string.Empty },
                new Query_Part_DetailResult._prop { key = "零件类型", value = partDetail.partType ?? string.Empty },
                new Query_Part_DetailResult._prop { key = "切断标记", value = partDetail.cutTab ?? string.Empty },
                new Query_Part_DetailResult._prop { key = "重量", value = partDetail.weight ?? string.Empty },
                new Query_Part_DetailResult._prop { key = "PPQ", value = partDetail.PPQ ?? string.Empty },
                new Query_Part_DetailResult._prop { key = "RPQ", value = partDetail.RPQ ?? string.Empty },
                new Query_Part_DetailResult._prop { key = "CPQ", value = partDetail.CPQ ?? string.Empty },
                new Query_Part_DetailResult._prop { key = "产品级别", value = partDetail.productGrade ?? string.Empty },
                new Query_Part_DetailResult._prop { key = "生效日期", value = partStartTime },
                new Query_Part_DetailResult._prop { key = "有效日期", value = partEndTime }
            };
            result.props = props.Where(q => !string.IsNullOrEmpty(q.value)).ToList();
            return result;
        }

        public override object QueryParts(DbRESTFulRepository repository, JObject param)
        {
            List<Query_PartsResult> result = repository.Invoke<Query_PartsResult>(CSIs.Query_Parts_For_Bmw, param);
            if (result == null || result.Count == 0)
            {
                return result;
            }
            int[] subGroupIds = new int[] { param.Value<int>(ParamFields._id) };
            List<Komm> kommList = repository.Invoke<Komm>(CSIs.Query_Komm_For_BMW, new { subGroupIds });
            var kommDic = kommList.GroupBy(q => q.komm_id).ToDictionary(k => k.Key, v => v.ToList());
            BuildPartResultForKomm(result, kommDic);
            string vin = param.Value<string>(ParamFields._vin);
            if (!string.IsNullOrEmpty(vin))
            {
                result = FilterPartsByVin(repository, result, vin, kommList, subGroupIds);
            }

            result = BuildPartsFinally(result);
            return result;
        }

        private List<Query_PartsResult> FilterPartsByVin(DbRESTFulRepository repository, List<Query_PartsResult> result, string vin, List<Komm> kommList, int[] subGroupIds)
        {
            var sequence = GetSequenceForVin(vin);
            string suffix = string.IsNullOrEmpty(sequence) ? "" : sequence.Substring(0, 2);
            var vinInfo = repository.Invoke(CSIs.Get_Model_By_Accuracy_Vin_For_Bmw, new { sequence, suffix });
            if (vinInfo == null)
            {
                vinInfo = repository.Invoke(CSIs.Get_Model_By_Vin_For_Bmw, new { sequence, suffix });
            }
            if (vinInfo == null) return result;
            string productionDateStr = vinInfo.productionDate;
            int productionDate = GetVinProdDate(productionDateStr);
            string week = vinInfo.week;
            IEnumerable<Feature> vinFeatures = repository.Invoke<Feature>(CSIs.Query_Vin_Features_For_Bmw, new { featureCodes = vinInfo.featureCodes?.Split(',') });
            if (vinFeatures.Count() == 0) return result;
            ISet<string> vinFeatureCodes = new HashSet<string>(vinFeatures.Select(x => x.featureCode));

            var filteredKommIds = filterKommIds(kommList, vinFeatureCodes);

            ISet<string> allKommIds = MergeListWithSpilt(result.Select(q => q.modelFilter));
            ISet<string> allExistsKommIds = new HashSet<string>();
            if (allKommIds.Any())
            {
                allExistsKommIds = new HashSet<string>(((List<Komm>)repository.Invoke<Komm>(CSIs.Query_Komm_By_Ids_For_BMW, new { ids = allKommIds })).Select(q => q.komm_id.ToString()));
            }
            ISet<int> notMatchPos = GetNoMatchPostions(repository, subGroupIds, week, productionDate, vin);
            //默认情况下，如果没有获取到以下信息，则所有配件全部属于此VIN码
            foreach (var item in result)
            {
                item.belongToVin = true;
                if (notMatchPos.Contains(item.sort))
                {
                    item.belongToVin = false;
                }
                FilterProductionDate(item, productionDate);
                FilterHand(item);
                FilterKommIds(item, filteredKommIds, allExistsKommIds);
            }

            var groups = result.Where(q => !string.IsNullOrEmpty(q.imageSN) && q.imageSN != _EmptySnSign).GroupBy(x => new { x.imageSN, x.versionCode });
            foreach (var group in groups)
            {
                BuildPartsForSameSnByDate(group.Where(x => x.belongToVin));
                BuildPartsForSameSnByFeature(group.Where(x => x.belongToVin));
            }
            BuildPartsForNoSN(result);
            return result;
        }

        private void BuildPartsForSameSnByDate(IEnumerable<Query_PartsResult> parts)
        {
            if (!parts.Any())
            {
                return;
            }
            foreach (var part in parts)
            {
                if (part.startTime.HasValue || part.endTime.HasValue)
                {
                    part.weight = 1;
                }
                else
                {
                    part.weight = 0;
                }
            }

            var maxWeight = parts.Max(q => q.weight);
            parts.Where(q => q.weight != maxWeight).ForEach(x => x.belongToVin = false);
        }

        private List<PartCatalogResult> FilterPartsByVin2(DbRESTFulRepository repository, List<PartCatalogResult> result, string vin, List<Komm> kommList, int[] subGroupIds)
        {
            var sequence = GetSequenceForVin(vin);
            string suffix = string.IsNullOrEmpty(sequence) ? "" : sequence.Substring(0, 2);
            var vinInfo = repository.Invoke(CSIs.Get_Model_By_Accuracy_Vin_For_Bmw, new { sequence, suffix });
            if (vinInfo == null)
            {
                vinInfo = repository.Invoke(CSIs.Get_Model_By_Vin_For_Bmw, new { sequence, suffix });
            }
            if (vinInfo == null) return result;
            string productionDateStr = vinInfo.productionDate;
            int productionDate = GetVinProdDate(productionDateStr);
            string week = vinInfo.week;
            IEnumerable<Feature> vinFeatures = repository.Invoke<Feature>(CSIs.Query_Vin_Features_For_Bmw, new { featureCodes = vinInfo.featureCodes?.Split(',') });
            if (vinFeatures.Count() == 0) return result;
            ISet<string> vinFeatureCodes = new HashSet<string>(vinFeatures.Select(x => x.featureCode));

            var filteredKommIds = filterKommIds(kommList, vinFeatureCodes);

            ISet<string> allKommIds = MergeListWithSpilt(result.Select(q => q.modelFilter));
            ISet<string> allExistsKommIds = new HashSet<string>();
            if (allKommIds.Any())
            {
                allExistsKommIds = new HashSet<string>(((List<Komm>)repository.Invoke<Komm>(CSIs.Query_Komm_By_Ids_For_BMW, new { ids = allKommIds })).Select(q => q.komm_id.ToString()));
            }
            ISet<int> notMatchPos = GetNoMatchPostions(repository, subGroupIds, week, productionDate, vin);
            //默认情况下，如果没有获取到以下信息，则所有配件全部属于此VIN码
            foreach (var item in result)
            {
                item.belongToVin = true;
                if (notMatchPos.Contains(item.sort))
                {
                    item.belongToVin = false;
                }
                FilterProductionDate(item, productionDate);
                FilterHand(item);
                FilterKommIds(item, filteredKommIds, allExistsKommIds);
            }

            var groups = result.Where(q => !string.IsNullOrEmpty(q.imageSN) && q.imageSN != _EmptySnSign && !string.IsNullOrEmpty(q.versionCode)).GroupBy(x => new { x.subGroupId, x.sn, x.versionCode });
            foreach (var group in groups)
            {
                BuildPartsForSameSnByDate(group.Where(x => x.belongToVin));
                BuildPartsForSameSnByFeature(group.Where(x => x.belongToVin));
            }
            BuildPartsForNoSN2(result);
            return result;
        }

        private List<Query_PartsResult> BuildPartsFinally(List<Query_PartsResult> result)
        {
            if (result == null || result.Count == 0) return result;
            Query_PartsResult currentInfo = result.First();
            List<Query_PartsResult> retList = new List<Query_PartsResult>();
            for (int i = 1; i < result.Count; i++)
            {
                if (result[i].imageSN == _EmptySnSign)
                {
                    retList.Add(currentInfo);
                    retList.Add(result[i]);
                    currentInfo = new Query_PartsResult() { imageSN = _NouseSnSign };
                }
                else if (EqualsUsage(currentInfo, result[i]))
                {
                    if (result[i].belongToVin)
                    {
                        currentInfo.belongToVin = true;
                    }
                }
                else
                {
                    retList.Add(currentInfo);
                    currentInfo = result[i];
                }
            }
            retList.Add(currentInfo);
            return retList.Where(q => q.imageSN != _NouseSnSign).ToList();
        }

        private bool EqualsUsage(Query_PartsResult currentInfo, Query_PartsResult query_PartsResult)
        {
            if (currentInfo == null || query_PartsResult == null)
            {
                return false;
            }
            return currentInfo.partCode == query_PartsResult.partCode &&
                currentInfo.partName == query_PartsResult.partName &&
                currentInfo.imageSN == query_PartsResult.imageSN &&
                currentInfo.quantity == query_PartsResult.quantity &&
                currentInfo.replacedPartCodes == query_PartsResult.replacedPartCodes &&
                currentInfo.notes == query_PartsResult.notes &&
                currentInfo.modelNotes == query_PartsResult.modelNotes;
        }

        private int GetVinProdDate(string productionDateStr)
        {
            productionDateStr = productionDateStr.Replace("-", "");
            if (productionDateStr.Length < 8)
            {
                productionDateStr = productionDateStr.PadRight(8, '0');
            }
            return productionDateStr.ToInt();
        }

        private ISet<string> MergeListWithSpilt(IEnumerable<string> splitList, char split = ',')
        {
            List<string> list = new List<string>();
            foreach (var str in splitList)
            {
                if (string.IsNullOrEmpty(str))
                {
                    continue;
                }
                list.AddRange(str.Split(split, StringSplitOptions.RemoveEmptyEntries));
            }
            return new HashSet<string>(list);
        }

        private void BuildPartsForNoSN(List<Query_PartsResult> result)
        {
            var currentParentSNResult = true;
            foreach (var item in result)
            {
                if (item.imageSN == _EmptySnSign && item.belongToVin)
                {
                    item.belongToVin = currentParentSNResult;
                }
                else
                {
                    currentParentSNResult = item.belongToVin;
                }
            }
        }
        private void BuildPartsForNoSN2(List<PartCatalogResult> result)
        {
            var currentParentSNResult = true;
            foreach (var item in result)
            {
                if (item.sn == _EmptySnSign && item.belongToVin)
                {
                    item.belongToVin = currentParentSNResult;
                }
                else
                {
                    currentParentSNResult = item.belongToVin;
                }
            }
        }

        private ISet<int> GetNoMatchPostions(DbRESTFulRepository repository, int[] subGroupIds, string week, int productionDate, string vin)
        {
            string vin47 = vin.Substring(3, 4);
            List<Position_Filter_Bmw> snFilters = repository.Invoke<Position_Filter_Bmw>(CSIs.Get_Position_Filter_For_Bmw, new { subGroupIds, week, vin47 });
            ISet<int> retSet = new HashSet<int>();
            productionDate = productionDate / 100;
            foreach (var filters in snFilters.GroupBy(q => q.pos))
            {
                foreach (var filter in filters)
                {
                    if (filter.relation == "FROM")
                    {
                        if (!string.IsNullOrEmpty(filter.vinString) && CompareVinStr(filter.vinString, vin) > 0)
                        {
                            retSet.Add(filter.pos);
                            break;
                        }
                        if (filter.date.HasValue && filter.date / 100 > productionDate)
                        {
                            retSet.Add(filter.pos);
                            break;
                        }
                        if (filter.productionDate.HasValue && filter.productionDate > productionDate)
                        {
                            retSet.Add(filter.pos);
                            break;
                        }
                    }
                    else if (filter.relation == "TO")
                    {
                        if (!string.IsNullOrEmpty(filter.vinString) && CompareVinStr(filter.vinString, vin) < 0)
                        {
                            retSet.Add(filter.pos);
                            break;
                        }
                        if (filter.date.HasValue && filter.date / 100 < productionDate)
                        {
                            retSet.Add(filter.pos);
                            break;
                        }
                        if (filter.productionDate.HasValue && filter.productionDate < productionDate)
                        {
                            retSet.Add(filter.pos);
                            break;
                        }
                    }
                }
            }
            return retSet;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fromStr"></param>
        /// <param name="vinCode"></param>
        /// <returns>
        /// fromStr - vinCode
        /// </returns>
        private int CompareVinStr(string fromStr, string vinCode)
        {
            var vinChars = vinCode.ToArray().TakeLast(fromStr.Length).ToList();
            for (int i = 0; i < fromStr.Length; i++)
            {
                char a = fromStr[i];
                if (a == '-')
                {
                    continue;
                }
                if (a < vinChars[i])
                {
                    return -1;
                }
                else if (a > vinChars[i])
                {
                    return 1;
                }
            }
            return 0;
        }

        private void FilterHand(Query_PartsResult item)
        {
            if (!item.belongToVin)
            {
                return;
            }
            if (item.hand?.ToUpper() == "R")
            {
                item.belongToVin = false;
            }
        }

        private IEnumerable<VinFeature> GetVinFeatures(DbRESTFulRepository repository, string sequence, string vin_from)
        {
            IEnumerable<VinFeature> vinFeatures = null;
            vinFeatures = repository.Invoke<VinFeature>(CSIs.Get_Parts_For_Vin_For_Bmw_Step2, new { vin_from = sequence });
            if (vinFeatures == null || vinFeatures.Count() == 0)
            {
                vinFeatures = repository.Invoke<VinFeature>(CSIs.Get_Parts_For_Vin_For_Bmw_Step2, new { vin_from });
            }
            return vinFeatures;
        }
        /// <summary>
        /// komm_id komm_pos komm_vz komm_code text
        /// 3302640 1                          用于车辆及
        /// 3302640 2        +       S502A     大灯清洗装置
        /// 
        /// 3290480 1                          用于车辆及
        /// 3290480 2        +       S508A     泊车距离报警系统(PDC)
        /// 
        /// 3306590 1                          用于车辆及
        /// 3306590 2        +       S502A     大灯清洗装置
        /// 3306590 3                          和
        /// 3306590 4        +       S508A     泊车距离报警系统(PDC)
        /// </summary>
        /// <param name="kommList">3302640，3290480，3306590</param>
        /// <param name="vinFeatureCodes">S502A，S508A</param>
        /// <returns>3306590</returns>
        private void BuildPartsForSameSnByFeature(IEnumerable<Query_PartsResult> parts)
        {
            if (parts.Any(x => !string.IsNullOrEmpty(x.featureCodes)))
            {
                parts.Where(x => string.IsNullOrEmpty(x.featureCodes)).ForEach(x => x.belongToVin = false);
            }
            else
            {
                return;
            }
            var orderedList = parts.Where(x => x.belongToVin).OrderBy(q => q.featureCodes?.Length).ToList();

            for (int i = orderedList.Count - 1; i > 0; i--)
            {
                var currentModel = orderedList[i];
                if (!currentModel.belongToVin)
                {
                    continue;
                }
                for (int j = i - 1; j >= 0; j--)
                {
                    var targetModel = orderedList[j];
                    if (!targetModel.belongToVin)
                    {
                        continue;
                    }
                    if (FeatureContains(currentModel.featureCodes, targetModel.featureCodes))
                    {
                        targetModel.belongToVin = false;
                        continue;
                    }
                }
            }
        }

        private bool FeatureContains(string featureCodes1, string featureCodes2)
        {
            var list1 = featureCodes1.Replace("+", "").Replace("-", "").Split(',');
            var list2 = featureCodes2.Replace("+", "").Replace("-", "").Split(',');
            if (list1.Length == list2.Length)
            {
                return false;
            }
            return !list2.Except(list1).Any();
        }

        private static readonly string _RemoveSign = "NeedRemoved";

        private void BuildSuperseesion(List<Query_PartsResult> result)
        {
            var shortPartCodes = result.Where(q => q.imageSN != _EmptySnSign).Select(q => q.partCode.Substring(4));
            for (int i = 0; i < result.Count; i++)
            {
                if (!string.IsNullOrEmpty(result[i].replacedPartCodes))
                {
                    var replacedPartCodes = result[i].replacedPartCodes.Split(',');
                    if (shortPartCodes.Intersect(replacedPartCodes).Any())
                    {
                        result[i].id = _RemoveSign;
                        while (i != result.Count - 1 && result[i + 1].imageSN == _EmptySnSign)
                        {
                            i++;
                            result[i].id = _RemoveSign;
                        }
                    }
                }
            }
            for (int i = result.Count - 1; i >= 0; i--)
            {
                if (result[i].id == _RemoveSign)
                {
                    result.RemoveAt(i);
                }
            }
        }

        private void FilterKommIds(Query_PartsResult item, ISet<string> filteredKommIds, ISet<string> allExsitIds)
        {
            if (!item.belongToVin) return;
            if (string.IsNullOrEmpty(item.modelFilter)) return;
            var ids = item.modelFilter.Split(',');
            if (ids.Any(q => !string.IsNullOrEmpty(q) && allExsitIds.Contains(q) && !filteredKommIds.Contains(q)))
            {
                item.belongToVin = false;
            }
            else
            {
                item.modelNotes = "";
            }
        }

        private void FilterProductionDate(Query_PartsResult item, int productionDate)
        {
            if (!item.belongToVin) return;
            if (item.startTime.HasValue && item.startTime.Value > productionDate)
            {
                item.belongToVin = false;
                return;
            }
            if (item.endTime.HasValue && item.endTime.Value <= productionDate)
            {
                item.belongToVin = false;
                return;
            }
            //if (!item.endTime.HasValue && !item.startTime.HasValue)
            //{
            //    if (item.partStartTime.HasValue && item.partStartTime.Value > productionDate)
            //    {
            //        item.belongToVin = false;
            //        return;
            //    }
            //    if (item.partEndTime.HasValue && item.partEndTime.Value <= productionDate)
            //    {
            //        item.belongToVin = false;
            //        return;
            //    }
            //}
        }


        private ISet<string> filterKommIds(List<Komm> kommList, ISet<string> vinFeatureCodes)
        {
            List<string> retList = new List<string>();
            var groups = kommList.GroupBy(x => x.komm_id);
            foreach (var group in groups)
            {
                StringBuilder sb = new StringBuilder();
                var isOk = true;
                foreach (var item in group)
                {
                    if (string.IsNullOrEmpty(item.komm_code))
                    {
                        var andRelation = !item.komm_text.StartsWith("或");
                        if (andRelation)
                        {
                            sb.Append('&');
                        }
                        else
                        {
                            sb.Append('|');
                        }
                    }
                    else
                    {
                        sb.Append(item.komm_vz).Append(item.komm_code);
                    }
                }

                var featuresList = sb.ToString().Split('&');
                foreach (var features in featuresList)
                {
                    if (string.IsNullOrEmpty(features))
                    {
                        continue;
                    }
                    var featureList = features.Split('|');
                    var flag = false;
                    if (featureList.Any(x => !string.IsNullOrEmpty(x)))
                    {
                        foreach (var feature in featureList)
                        {
                            if (string.IsNullOrEmpty(feature))
                            {
                                continue;
                            }
                            if (feature.StartsWith('-'))
                            {
                                if (!vinFeatureCodes.Contains(feature.TrimStart('-')))
                                {
                                    flag = true;
                                    break;
                                }
                            }
                            else
                            {
                                if (vinFeatureCodes.Contains(feature.TrimStart('+')))
                                {
                                    flag = true;
                                    break;
                                }
                            }
                        }
                    }
                    else
                    {
                        flag = true;
                    }

                    if (!flag)
                    {
                        isOk = false;
                        break;
                    }
                }

                if (isOk)
                {
                    retList.Add(group.Key.ToString());
                }
            }
            return new HashSet<string>(retList);
        }

        private void BuildPartResultForKomm(List<Query_PartsResult> results, Dictionary<int, List<Komm>> kommDic)
        {
            foreach (var result in results)
            {
                if (string.IsNullOrEmpty(result.modelFilter))
                {
                    continue;
                }
                string[] kommCodes = result.modelFilter.Split(',');
                foreach (var kommCode in kommCodes)
                {
                    if (string.IsNullOrEmpty(kommCode))
                    {
                        continue;
                    }
                    int kommId = kommCode.ToInt();

                    if (kommDic.ContainsKey(kommId))
                    {
                        var tempKomms = kommDic[kommId];
                        foreach (var temoKomm in tempKomms)
                        {
                            result.modelNotes += temoKomm.komm_text;
                            if (!string.IsNullOrEmpty(temoKomm.komm_code))
                            {
                                result.modelNotes += $"({temoKomm.komm_code}={(temoKomm.komm_vz == "+" ? "是" : "否")})";
                                result.featureCodes += "," + temoKomm.komm_vz + temoKomm.komm_code;
                            }
                        }
                    }
                }
                result.featureCodes = result.featureCodes?.Trim(',');
            }
        }
        private void BuildPartResultForKomm2(List<PartCatalogResult> results, Dictionary<int, List<Komm>> kommDic)
        {
            foreach (var result in results)
            {
                if (string.IsNullOrEmpty(result.modelFilter))
                {
                    continue;
                }
                string[] kommCodes = result.modelFilter.Split(',');
                foreach (var kommCode in kommCodes)
                {
                    if (string.IsNullOrEmpty(kommCode))
                    {
                        continue;
                    }
                    int kommId = kommCode.ToInt();

                    if (kommDic.ContainsKey(kommId))
                    {
                        var tempKomms = kommDic[kommId];
                        foreach (var temoKomm in tempKomms)
                        {
                            result.modelNotes += temoKomm.komm_text;
                            if (!string.IsNullOrEmpty(temoKomm.komm_code))
                            {
                                result.modelNotes += $"({temoKomm.komm_code}={(temoKomm.komm_vz == "+" ? "是" : "否")})";
                                result.featureCodes += "," + temoKomm.komm_vz + temoKomm.komm_code;
                            }
                        }
                    }
                }
                result.featureCodes = result.featureCodes?.Trim(',');
            }
        }
        protected override dynamic GetModelsByPart(DbRESTFulRepository repository, int pageIndex, int pageSize, string brandCode, string partCode)
        {
            int defaultTimeOut = CSILoader.Instance.Setting.CommandTimeout;
            CSILoader.Instance.Setting.CommandTimeout = 10;
            try
            {
                var ret = repository.Invoke(CSIs.Query_Models_By_Part_For_Bmw, new { pageIndex, pageSize, brandCode, partCode });
                CSILoader.Instance.Setting.CommandTimeout = defaultTimeOut;
                return ret;
            }
            catch
            {
                CSILoader.Instance.Setting.CommandTimeout = defaultTimeOut;
                LoggerManager.Logger.LogInformation($"{brandCode} {partCode}查询适用车型超时！");
                return null;
            }
        }

        protected override List<Query_MainGroupResult> FilterMainGroupsByVin(DbRESTFulRepository repository, List<Query_MainGroupResult> results, string vin)
        {
            if (string.IsNullOrEmpty(vin)) return results;

            var sequence = GetSequenceForVin(vin);
            if (sequence != null)
            {
                string suffix = sequence.Substring(0, 2);
                var model = repository.Invoke(CSIs.Get_Model_By_Accuracy_Vin_For_Bmw, new { sequence, suffix });
                if (model == null)
                {
                    model = repository.Invoke(CSIs.Get_Model_By_Vin_For_Bmw, new { sequence, suffix });
                }
                if (model != null)
                {
                    string trans = model.transmission;
                    if (!string.IsNullOrEmpty(trans))
                    {
                        if (trans.StartsWith("A"))
                        {
                            results = results.Where(q => q.code != "23").ToList();
                        }
                        else if (trans.StartsWith("M"))
                        {
                            results = results.Where(q => q.code != "24").ToList();
                        }
                    }
                }
            }
            return results;
        }

        protected override dynamic GetHotPointsByPart(DbRESTFulRepository repository, string brandCode, string partCode, IEnumerable<string> imageIds)
        {
            return repository.Invoke(CSIs.Query_HotPoints_By_Part_For_Bmw, new { brandCode, partCode, imageIds });
        }
        public override object QuerySubGroup(DbRESTFulRepository repository, JObject param)
        {
            param.RemoveEmptyValues();
            var id = param.Value<string>(ParamFields._id);
            var brandCode = param.Value<string>(ParamFields._brandCode);
            //var selectorId = param.Value<string>(ParamFields._selectorId);
            List<Query_SubGroupResult> subGroups = repository.Invoke<Query_SubGroupResult>(CSIs.Query_SubGroup_For_Bmw, new { id, brandCode });
            foreach (var item in subGroups)
            {
                item.belongTo = true;
            }
            var vin = param.Value<string>("vin");
            if (!string.IsNullOrEmpty(vin))
            {
                FilterSubGroupsByVin(repository, subGroups, param);
            }
            return subGroups;
        }

        protected override List<Query_SubGroupResult> FilterSubGroupsByVin(DbRESTFulRepository repository, List<Query_SubGroupResult> subGroups, JObject param)
        {
            var vin = param.Value<string>(ParamFields._vin);
            if (string.IsNullOrEmpty(vin) || subGroups == null || subGroups.Count == 0)
            {
                return subGroups;
            }
            var sequence = GetSequenceForVin(vin);
            string suffix = string.IsNullOrEmpty(sequence) ? "" : sequence.Substring(0, 2);
            var vinInfo = repository.Invoke(CSIs.Get_Model_By_Accuracy_Vin_For_Bmw, new { sequence, suffix });
            if (vinInfo == null)
            {
                vinInfo = repository.Invoke(CSIs.Get_Model_By_Vin_For_Bmw, new { sequence, suffix });
            }
            if (vinInfo == null) return subGroups;
            string productionDateStr = vinInfo.productionDate;
            int productionDate = GetVinProdDate(productionDateStr);
            string week = vinInfo.week;
            IEnumerable<Feature> vinFeatures = repository.Invoke<Feature>(CSIs.Query_Vin_Features_For_Bmw, new { featureCodes = vinInfo.featureCodes?.Split(',') });
            if (vinFeatures.Count() == 0) return subGroups;
            ISet<string> vinFeatureCodes = new HashSet<string>(vinFeatures.Select(x => x.featureCode));

            List<Komm> kommList = repository.Invoke<Komm>(CSIs.Query_SubGroup_Komm_By_Ids_For_BMW, new { subGroupIds = subGroups.Select(q => q.id) });

            var dic = kommList.GroupBy(x => x.sub_group_code).ToDictionary(x => x.Key, v => v.ToList());

            ISet<string> allKommIds = MergeListWithSpilt(subGroups.Select(q => q.applicableModel));
            ISet<string> allExistsKommIds = new HashSet<string>();
            if (allKommIds.Any())
            {
                allExistsKommIds = new HashSet<string>(((List<Komm>)repository.Invoke<Komm>(CSIs.Query_Komm_By_Ids_For_BMW, new { ids = allKommIds })).Select(q => q.komm_id.ToString()));
            }

            Dictionary<string, List<SubGroupFilterForBmw>> dicSubGroupFilter = ((IEnumerable<SubGroupFilterForBmw>)repository.Invoke<SubGroupFilterForBmw>(CSIs.Get_SubGroup_Filter_For_BMW, new { subGroupCodes = subGroups.Select(q => q.code) })).GroupBy(q => q.subGroupCode).ToDictionary(k => k.Key, v => v.ToList());

            for (int i = 0; i < subGroups.Count; i++)
            {
                var subGroup = subGroups[i];
                if (subGroup.startTime.HasValue && subGroup.startTime.Value > productionDate)
                {
                    subGroups.RemoveAt(i);
                    i--;
                    continue;
                }
                if (subGroup.endTime.HasValue && subGroup.endTime.Value <= productionDate)
                {
                    subGroups.RemoveAt(i);
                    i--;
                    continue;
                }
                if (subGroup.remark == "R")
                {
                    subGroup.belongTo = false;
                    continue;
                }
                if (dicSubGroupFilter.ContainsKey(subGroup.code))
                {
                    foreach (var filter in dicSubGroupFilter[subGroup.code])
                    {
                        if ((filter.operatorCode == "TO" && productionDate > filter.date) || (filter.operatorCode == "FROM" && productionDate < filter.date))
                        {
                            subGroup.belongTo = false;
                            break;
                        }
                    }
                    if (!subGroup.belongTo)
                    {
                        continue;
                    }
                }
                if (string.IsNullOrEmpty(subGroup.applicableModel) || !allExistsKommIds.Contains(subGroup.applicableModel))
                {
                    continue;
                }
                var filterList = subGroup.applicableModel.Split(',');
                var suitList = filterKommIds(dic[subGroup.code], vinFeatureCodes);
                foreach (var filter in filterList)
                {
                    if (!suitList.Contains(filter))
                    {
                        subGroup.belongTo = false;
                        break;
                    }
                }
            }
            return subGroups;
        }

        protected override List<PartCatalog> GetPartCatalogs(DbRESTFulRepository repository, IEnumerable<string> partCodes, string selectorId, string brandCode, string vinCode)
        {
            List<PartCatalogResult> partCatalogResults = repository.Invoke<PartCatalogResult>(CSIs.Query_Part_Catalog_For_Bmw, new { selectorId });
            int[] subGroupIds = partCatalogResults.Select(q => q.subGroupId).Distinct().ToArray();
            List<Komm> kommList = repository.Invoke<Komm>(CSIs.Query_Komm_For_BMW, new { subGroupIds });
            var kommDic = kommList.GroupBy(q => q.komm_id).ToDictionary(k => k.Key, v => v.ToList());
            BuildPartResultForKomm2(partCatalogResults, kommDic);
            partCatalogResults = FilterPartsByVin2(repository, partCatalogResults, vinCode, kommList, subGroupIds).Where(q => q.belongToVin).ToList();

            var hotPointsModel = repository.Invoke(CSIs.Query_HotPoints_By_SubGroups_For_Bmw, new { brandCode, subGroupIds = partCatalogResults.Select(q => q.subGroupId).Distinct() });
            JArray allPoints = JArray.FromObject(hotPointsModel);

            foreach (var item in partCatalogResults)
            {
                //2.1适用车型多张图片处理
                var imgs = allPoints.Where(p => p.Value<int>("subGroupId") == item.subGroupId).Select(p => p.Value<string>("url")).Distinct().Select(x => new _imgs { url = x }).ToList();
                foreach (var img in imgs)
                {
                    //2.1.1适用车型每张图片对应的热点处理
                    img.points = allPoints.Where(p => p.Value<string>("url") == img.url && p.Value<int>("subGroupId") == item.subGroupId && p.Value<string>("sn") == item.sn)
                                       .Select(p => p.ToObject<_imgs._points>()).ToList();
                }
                item.imgs = imgs;
            }
            return BuildPartCatalogList(partCatalogResults);
        }

        private string GetSequenceForVin(string vin)
        {
            if (string.IsNullOrEmpty(vin))
            {
                return "";
            }
            string sequence = null;
            if (vin.Length == 17) sequence = vin.Right(7);
            else if (vin.Length == 7) sequence = vin;
            return sequence;
        }

        public override object RealTimeQueryOEMStock(DbRESTFulRepository repository, JObject param)
        {
            EPCOnlineQueryService epcOnlineQueryService = new EPCOnlineQueryService();
            string partCode = param.Value<string>(ParamFields._partCode);
            string brandCode = param.Value<string>(ParamFields._brandCode);
            var queryRes = epcOnlineQueryService.RealTimeQueryPartPriceAndStock(partCode, brandCode);

            BmwStockRes stockInfo = CheckOnlinePriceAndStockQueryRes(queryRes);

            return HandleStockResult(stockInfo);
        }

        //{hasError: false, errorMessage: '', data: {vin: task.params.vin, result01: rst, result02: rst01}}
        BmwStockRes CheckOnlinePriceAndStockQueryRes(JObject queryRes)
        {
            if (queryRes == null)
            {
                LoggerManager.Logger.LogError("BmwQueryInterceptor.GetEpcOnlineVin, queryRes is null");

                throw new Exception($"系统繁忙，请稍后再试！");
            }
            bool hasError = queryRes["HasError"].ToObject<bool>();
            if (hasError)
            {
                string errMsg = queryRes["ErrorMessage"].ToObject<string>();
                LoggerManager.Logger.LogError($"BmwQueryInterceptor.GetEpcOnlineVin, hasError,{errMsg} ");

                throw new Exception($"系统繁忙，请稍后再试！");
            }
            var data = queryRes["Data"];
            if (data == null)
            {
                LoggerManager.Logger.LogError("BmwQueryInterceptor.GetEpcOnlineVin, data is null");

                throw new Exception($"系统繁忙，请稍后再试！");
            }
            BmwStockRes res = data.ToObject<BmwStockRes>();
            return res;
        }

        object HandleStockResult(BmwStockRes stockInfo)
        {
            if (stockInfo == null)
            {
                return null;
            }
            string taxRateStr = ConfigurationManager.Settings.Variables["TaxRate"];
            decimal taxRate = 1.13m;
            try
            {
                taxRate = Convert.ToDecimal(taxRateStr);
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.LogError(ex, "HandleStockResult.taxRate");
            }

            decimal price = 0, cost = 0;
            DateTime priceUpdatedAt = DateTime.Now;
            int stock = 0;
            if (stockInfo.prices != null && stockInfo.prices.Any())
            {
                foreach (var item in stockInfo.prices)
                {
                    if (item.type == "sale")
                    {
                        price = item.price > 0 ? item.price * taxRate : item.price;
                    }
                    else if (item.type == "cost")
                    {
                        cost = item.price > 0 ? item.price * taxRate : item.price;
                    }
                }
            }
            if (stockInfo.stocks != null && stockInfo.stocks.Any())
            {
                stock = stockInfo.stocks.Sum(e => e.stock);
            }
            return new { price, cost, priceUpdatedAt, stock };
        }
    }
}
